package util;

import com.alibaba.fastjson.JSONObject;
import main.original.YGFutureTechIndexKline;
import main.talib.YGFutureTechIndexTaLibSMA;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class YGFutureMaiToJava {

    //如果当前K线之前没有shift根K线时不计算，若果当前K线前有shift根K线则求当前K线之前shift根那个k线的收盘价
    public static Double getREF(List<YGFutureTechIndexKline> klines,int shift){

        Double close = 0.0;
        if(shift>=klines.size()){
           return null;
        }else {
            close = klines.get(klines.size()-1-shift).getM_close_price();
        }
        return close;
    }

    public static Double getREF_DOUBLE(List<Double> klines,int shift){

        Double close = 0.0;
        if(shift>=klines.size()){
            return null;
        }else {
            close = klines.get(klines.size()-1-shift);
        }
        return close;
    }

    //当传入的K线从未被ref计算过时，如果shift=0则取第一根K线的closeprice，
    public static List<Double> getREFs(List<YGFutureTechIndexKline> klines,int shift){
        List<Double> closeArry = new ArrayList<>();
        double close = 0.0;
        if(shift>=klines.size()-1){
            return null;
        }else {
            for(int i=0;i<klines.size();i++){
                if(i>klines.size()-1-shift){
                   break;
                }else {
                    closeArry.add(klines.get(i).getM_close_price());
                }
            }
            return closeArry;
        }
    }

    //当K线数量不够optInTimePeriod根时取已有K线的收盘价最大值，直到k线足够时k线的选择空间向后移动，LLV相同（这个方法计算是包含当前K线在内的）
    public static double getHHV(List<YGFutureTechIndexKline> klines,int optInTimePeriod,String item){
        int size = klines.size();
        List<Double> hightArry = new ArrayList<>();
        int startindex = 0;
        if(size<=optInTimePeriod){
            startindex = 0;
        }else {
            startindex = size - optInTimePeriod;
        }

        if(item.equals("close")==true){
            for(int i = startindex;i<size;i++){

                hightArry.add(klines.get(i).getM_close_price());
            }
        }else if(item.equals("open")==true){
            for(int i = startindex;i<size;i++){

                hightArry.add(klines.get(i).getM_open_price());
            }
        }else if(item.equals("hight")==true){
            for(int i = startindex;i<size;i++){

                hightArry.add(klines.get(i).getM_max());
            }
        }else if(item.equals("low")==true){
            for(int i = startindex;i<size;i++){

                hightArry.add(klines.get(i).getM_min());
            }
        }
        return Collections.max(hightArry);
    }

    public static List<Double> getHHVs(List<YGFutureTechIndexKline> klines,int optInTimePeriod){
        List<Double> HHVs = new ArrayList<>();
        int startIndex = 0;
        int endIndex = optInTimePeriod;
        int size = klines.size();

        for(int i=0;i<size;i++){
            if(endIndex>size){
                break;
            }else {
                if(endIndex<optInTimePeriod-1){
                    List<YGFutureTechIndexKline> subklines  = klines.subList(startIndex,endIndex);
                    List<Double> hightArry = subklines.stream().map(YGFutureTechIndexKline::getM_max).collect(Collectors.toList());
                    HHVs.add( Collections.max(hightArry));
                    endIndex = endIndex + 1;
                }else {
                    List<YGFutureTechIndexKline> subklines  = klines.subList(startIndex,endIndex);
                    List<Double> hightArry = subklines.stream().map(YGFutureTechIndexKline::getM_max).collect(Collectors.toList());
                    HHVs.add( Collections.max(hightArry));
                    startIndex = startIndex + 1;
                    endIndex = endIndex + 1;
                }
            }
        }
        return HHVs;
    }

    public static double getLLV(List<YGFutureTechIndexKline> klines,int optInTimePeriod,String item){
        int size = klines.size();
        List<Double> lowestArry = new ArrayList<>();
        int startindex = 0;
        if(size<=optInTimePeriod){
            startindex = 0;
        }else {
            startindex = size - optInTimePeriod;
        }

            if(item.equals("close")==true){
                for(int i = startindex;i<size;i++){
                    lowestArry.add(klines.get(i).getM_close_price());
                }
            }else if(item.equals("open")==true){
                for(int i = startindex;i<size;i++){
                    lowestArry.add(klines.get(i).getM_open_price());
                }
            }else if(item.equals("hight")==true){
                for(int i = startindex;i<size;i++){
                    lowestArry.add(klines.get(i).getM_max());
                }
            }else if(item.equals("low")==true){
                for(int i = startindex;i<size;i++){
                    lowestArry.add(klines.get(i).getM_min());
                }
            }
        return Collections.min(lowestArry);
    }

    public static List<Double> getLLVs(List<YGFutureTechIndexKline> klines,int optInTimePeriod){
        List<Double> LLVs = new ArrayList<>();
        int startIndex = 0;
        int endIndex = optInTimePeriod;
        int size = klines.size();

        for(int i=0;i<size;i++){
            if(endIndex>size){
                break;
            }else {
                if(endIndex<optInTimePeriod-1){
                    List<YGFutureTechIndexKline> subklines  = klines.subList(startIndex,endIndex);
                    List<Double> hightArry = subklines.stream().map(YGFutureTechIndexKline::getM_min).collect(Collectors.toList());
                    LLVs.add( Collections.max(hightArry));
                    endIndex = endIndex + 1;
                }else {
                    List<YGFutureTechIndexKline> subklines  = klines.subList(startIndex,endIndex);
                    List<Double> hightArry = subklines.stream().map(YGFutureTechIndexKline::getM_min).collect(Collectors.toList());
                    LLVs.add( Collections.max(hightArry));
                    startIndex = startIndex + 1;
                    endIndex = endIndex + 1;
                }

            }

        }
        return LLVs;
    }

    //求包含当前K线和之前k线共optInTimePeriod根K收盘价的和，如果K线数量不够则对已有的所有k线收盘价求和
    public static double getSUM(List<Double> numbers,int optInTimePeriod){
        double sum = 0;
        if(numbers == null|| numbers.size()==0){
            return sum;
        }
        int size = numbers.size();
        if(optInTimePeriod == 0){
            for(int i = 0;i<size;i++){
                sum = sum+numbers.get(i);
            }
        }else {
            if(optInTimePeriod>=numbers.size()){
                for(int i = 0;i<size;i++){
                    sum = sum+numbers.get(i);
                }
            }else {
                for(int i = size - optInTimePeriod;i<size;i++){
                    sum = sum+numbers.get(i);
                }
            }
        }

        return sum;
    }

    //求包含当前K线和之前k线共optInTimePeriod根K收盘价的和，如果K线数量不够则对已有的所有k线收盘价求和
    public static double getSUMinter(List<Integer> numbers,int optInTimePeriod){
        int size = numbers.size();
        double sum = 0;
        if(numbers.size()==0 || numbers == null){
            return sum;
        }
        if(optInTimePeriod == 0){
            for(int i = 0;i<size;i++){
                sum = sum+numbers.get(i);
            }
        }else {
            if(optInTimePeriod>=numbers.size()){
                for(int i = 0;i<size;i++){
                    sum = sum+numbers.get(i);
                }
            }else {
                for(int i = size - optInTimePeriod;i<size;i++){
                    sum = sum+numbers.get(i);
                }
            }
        }

        return sum;
    }


    //SMA(N)=SMA(N-1)*(N-M)/N+X(N)*M/N  计算第一根K线时以其收盘价作为sma值之后以第二根K线的收盘价做X(N)（在此方法中即以第一个double数本身做sma值）
    public static Double getSMA(Double number,int optInTimePeriod,Double preSmaValue,Double weight){
        Double smaValue;

        if(optInTimePeriod <= 0){
            smaValue = null;
        }else{
            if(preSmaValue == null){
                smaValue = number;
            }else {
                smaValue = (preSmaValue*(optInTimePeriod-weight)/optInTimePeriod) +  number*weight/optInTimePeriod;
            }

        }
        return smaValue;
    }

    //SMA(N)=SMA(N-1)*(N-M)/N+X(N)*M/N
    public static List<Double> getSMAs(List<Double> numbers,int optInTimePeriod,Double weight){
        List<Double> SMAs = new ArrayList<>();
        int size = numbers.size();

        for(int i=0;i<size;i++){

            if(i==0){
                double sma_value = numbers.get(i);
                SMAs.add(sma_value);
            }else {
                double sma_value = (numbers.get(i-1)*(optInTimePeriod-weight)/optInTimePeriod) + (numbers.get(i)*weight/optInTimePeriod);
                SMAs.add(sma_value);
            }

        }
        return SMAs;
    }

    //包含当前K线在内向前数optInTimePeriod跟K线的收盘价或其它项的平均值，如果K线数量不够则不计算
    public static Double getMA(List<Double> numbers,int optInTimePeriod){
        Double maValue = 0.0;
        if(numbers.size()<optInTimePeriod){
            maValue = null;
        }else if(numbers.size() >= optInTimePeriod){
            int number_size = numbers.size();
            List<Double> sub_numbers = numbers.subList(number_size-optInTimePeriod,number_size);
            double sum = sub_numbers.stream().collect(Collectors.summingDouble(x -> x));
             maValue = sum/optInTimePeriod;
        }else {
            maValue = null;
        }
        return maValue;
    }

    //文华上CROSS原理是上次K线中两个值的状态和和当前K线两个值的状态比较，如果相反则产生了交叉
    //CROSS方法判断db上穿cb，即db在上一根K线中是小于等于cb，当前K线中db大于cb
    //如果当前K线完成则这两根K线的状态下的上穿状态不再变化，否则状态跟随当前K线的db与cb值变化。
    public static boolean getCross(List<Double> db_lst,List<Double> cb_lst) {
        if(db_lst!=null && cb_lst!=null){
            if(db_lst.size() >=2 && cb_lst.size() >= 2){
                Integer db_size = db_lst.size();
                Integer cb_size = cb_lst.size();

                if(db_lst.get(db_size-2)<=cb_lst.get(cb_size-2)){

                    if(db_lst.get(db_size-1)>cb_lst.get(cb_size-1)){
                        return true;
                    }else {
                        return false;
                    }

                }else {
                    return false;
                }
            }else {
                return false;
            }
        }else{
            return false;
        }

    }

   /*
        首先从您第一根K线算起，看每根K线的最高价，和第一根K线的最高价相比是不是涨幅超过10%
    （这里假设第一根K线的最高价位置比较低，位置高的画线原理是一样的），把第一次涨幅超过10%
    的这个点成为上拐点，然后K线继续往后走，出现了一个最高点，然后开始下跌了，这个最高点我们记为A，
    然后K线往后走的跌幅是否超过10%，把第一次跌幅超过10%的K线记为下拐点，如果这个下拐点成立了，
    那么之前的A的这个位置就发生一次转向，就是我们看到的折线，如下图，最低点位置开始，上面一条横线就是上拐点位置，
    然后出现了最高点，再往下走，出现了下拐点，那么证明最高点处应该转折，之后都是按照这个原理画的。
    zig函数产生的连线是连接的最大值和最小值
    */
    public static List<Double> getZigZag(List<Double> numbers, Double percentage){
        //     循环前的变量初始化
        //     端点 候选点 扫描点 端点列表 拐点线列表 趋势状态
        int ZIG_STATE_START = 0;
        int ZIG_STATE_RISE = 1;
        int ZIG_STATE_FALL = 2;
        int peer_i = 0;
        Integer candidate_i = 0;
        int scan_i = 0;
        List<Integer> peers = new ArrayList<>();
        List<Double> ziglst = new ArrayList<>();
        int state = ZIG_STATE_START;
        percentage = percentage/100;
        ////////////////////////////////////////////////////////////////////
        if(numbers.size()<2){
            return null;
        }
        while (true) {
            scan_i += 1;
            if (scan_i == numbers.size() - 1) {
                // 扫描到尾部
                if (candidate_i == null) {
                    peer_i = scan_i;
                    peers.add(peer_i);
                } else {
                    if (state == ZIG_STATE_RISE) {
                        if (numbers.get(scan_i) >= numbers.get(candidate_i)) {
                            peer_i = scan_i;
                            peers.add(peer_i);
                        } else {
                            peer_i = candidate_i;
                            peers.add(peer_i);
                            peer_i = scan_i;
                            peers.add(peer_i);
                        }
                    } else if (state == ZIG_STATE_FALL) {
                        if (numbers.get(scan_i) <= numbers.get(candidate_i)) {
                            peer_i = scan_i;
                            peers.add(peer_i);
                        } else {
                            peer_i = candidate_i;
                            peers.add(peer_i);
                            peer_i = scan_i;
                            peers.add(peer_i);
                        }
                    }
                }
                break;
            }

            ///////////////////////////////////////////////////////////////////////////
            if (state == ZIG_STATE_START) {
                if (numbers.get(scan_i) >= numbers.get(peer_i) * (1 + percentage)) {
                    candidate_i = scan_i;
                    state = ZIG_STATE_RISE;
                } else if (numbers.get(scan_i) <= numbers.get(peer_i) * (1 - percentage)) {
                    candidate_i = scan_i;
                    state = ZIG_STATE_FALL;
                }
                peers.add(peer_i);
            } else if (state == ZIG_STATE_RISE) {
                if (numbers.get(scan_i) >= numbers.get(candidate_i)) {
                    candidate_i = scan_i;
                } else if (numbers.get(scan_i) <= numbers.get(candidate_i) * (1 - percentage)) {
                    peer_i = candidate_i;
                    peers.add(peer_i);
                    state = ZIG_STATE_FALL;
                    candidate_i = scan_i;
                }
            } else if (state == ZIG_STATE_FALL) {
                if (numbers.get(scan_i) <= numbers.get(candidate_i)) {
                    candidate_i = scan_i;
                } else if (numbers.get(scan_i) >= numbers.get(candidate_i) * (1 + percentage)) {
                    peer_i = candidate_i;
                    peers.add(peer_i);
                    state = ZIG_STATE_RISE;
                    candidate_i = scan_i;
                }
            }
        }
////////////////////////////////////////////////////////////////
        //线性插值， 计算出zig的值
        for(int i=1;i<peers.size();i++){

            int  peer_start_i = peers.get(i-1);
            int peer_end_i = peers.get(i);
            Double start_value = numbers.get(peer_start_i);
            Double end_value = numbers.get(peer_end_i);
            Double a = (end_value - start_value)/(peer_end_i - peer_start_i);// 斜率

            if(peer_start_i == 0){
                for(int j=0;j<(peer_end_i - peer_start_i +1);j++){
                    ziglst.add(start_value + a*j);
                }
            }else {
                for(int j=1;j<(peer_end_i - peer_start_i +1);j++){
                    ziglst.add(start_value + a*j);
                }
            }
        }
        return ziglst;
    }

    //EMA(N)=2X(N)/(N+1) + (N-1)*EMA(N-1)/(N+1)  计算第一根K线时以其收盘价作为ema值之后以第二根K线的收盘价做X(N)（在此方法中即以第一个double数本身做ema值）
    public static Double getEMA(Double number,int optInTimePeriod,Double preEmaValue){
        Double emaValue;

        if(optInTimePeriod <= 0){
            emaValue = null;
        }else{
            if(preEmaValue == null){
                emaValue = number;
            }else {
                emaValue = 2*number/(optInTimePeriod+1) + (optInTimePeriod-1)*preEmaValue/(optInTimePeriod+1);
            }

        }
        return emaValue;
    }



    //求两根K向某一项价格（如收盘价）的斜率：X(size-1)-X(size-N)
    public static Double getSLOPE(List<Double> numbers,int optInTimePeriod){
        int size = numbers.size();
        Double slopeValue;

        if(optInTimePeriod <= 0){
            slopeValue = null;
        }else{
            if(numbers.size() < optInTimePeriod){
                slopeValue = null;
            }else {
                slopeValue = (numbers.get(size-1)-numbers.get(size-optInTimePeriod))/(optInTimePeriod-1);
            }
        }
        return slopeValue;
    }


    //本方法中troughPositionlst存储所有波谷的位置，peakPositionlst存储所有波峰的位置
    public static  List<Integer> getTROUGHBARS(List<Double> numbers, Double percentage) {
        int ZIG_STATE_START = 0;
        int ZIG_STATE_RISE = 1;
        int ZIG_STATE_FALL = 2;
        int state = ZIG_STATE_START;
        List<Double> troughBarslst = getZigZag(numbers, percentage);
        List<Integer> troughPositionlst = new ArrayList<>();
        List<Integer> peakPositionlst = new ArrayList<>();

        if (troughBarslst != null && troughBarslst.size() > 0) {
            for (int i = 1; i < troughBarslst.size(); i++) {
                if (i == troughBarslst.size() - 1) {
                    // 扫描到尾部
                    if (state == ZIG_STATE_RISE) {
                        if (troughBarslst.get(i) <= troughBarslst.get(i - 1)) {
                            troughPositionlst.add(i);
                            peakPositionlst.add(i - 1);
                        }
                    }
                     else if (state == ZIG_STATE_FALL) {
                            if (troughBarslst.get(i) >= troughBarslst.get(i - 1)) {
                                troughPositionlst.add(i - 1);
                                peakPositionlst.add(i);
                            }
                     }


                    break;
                }
   ///////////////////////////////////////////////////////////////////////////////////////////////////////
                    if (state == ZIG_STATE_START) {
                        if (troughBarslst.get(i) >= troughBarslst.get(i - 1)) {
                            state = ZIG_STATE_RISE;
                            troughPositionlst.add(i - 1);
                        } else if (troughBarslst.get(i) <= troughBarslst.get(i - 1)) {
                            state = ZIG_STATE_FALL;
                            peakPositionlst.add(i - 1);
                        }
                    } else if (state == ZIG_STATE_RISE) {
                        if (troughBarslst.get(i) <= troughBarslst.get(i - 1)) {
                            state = ZIG_STATE_FALL;
                            peakPositionlst.add(i - 1);
                        }
                    } else if (state == ZIG_STATE_FALL) {
                        if (troughBarslst.get(i) >= troughBarslst.get(i - 1)) {
                            state = ZIG_STATE_RISE;
                            troughPositionlst.add(i - 1);
                        }
                    }
                }
            }
        else {
                return null;
        }
        return troughPositionlst;

    }
}
